#include <GL/glut.h>
#include <cmath>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include "../include/Callbacks.h"
#include "../include/Image.h"
#include "../include/Masque.h"
#include "../include/Pixel.h"
#include "../include/vecteur.h"
#include "../include/Matrice.h"
#include "../include/InpaintingThese.h"

#define GLUT_WHEEL_UP 3
#define GLUT_WHEEL_DOWN 4
int Callbacks::hauteur = 0;
int Callbacks::largeur = 0;
int Callbacks::MousePrevX = 0;
int Callbacks::MousePrevY = 0;
int Callbacks::modeDessin = 0;
Image* Callbacks::im = NULL;
Image* Callbacks::u = NULL;
Image* Callbacks::u1 = NULL;
Image* Callbacks::laplacien = NULL;
Image* Callbacks::laplacienU = NULL;
Image* Callbacks::gradienX = NULL;
Image* Callbacks::gradienY = NULL;
Matrice* Callbacks::hessienne = NULL;
Masque* Callbacks::m = NULL;
bool bAfficherMasque = true;
InpaintingThese inp;

using namespace std;


void Callbacks::redim(int largeur, int hauteur)
{
	glViewport(0, 0, largeur, hauteur) ;
	glMatrixMode(GL_PROJECTION) ;
	glLoadIdentity() ;
	gluOrtho2D(0, largeur, 0, hauteur) ;
	glMatrixMode(GL_MODELVIEW) ;
	glLoadIdentity() ;
}

void Callbacks::souris (int bouton, int etat, int x, int y)
{
	switch (bouton)
	{
		case GLUT_LEFT_BUTTON:
			if (etat==GLUT_DOWN)
			{
				m->Dessine(x,hauteur-y,MousePrevX,MousePrevY,255);
				modeDessin = 0;
			}
			break ;
		case GLUT_RIGHT_BUTTON:
			if (etat==GLUT_DOWN)
			{
				m->Dessine(x,hauteur-y,MousePrevX,MousePrevY,0);
				modeDessin = 1;
			}
			break;
		case GLUT_WHEEL_UP:
			m->setTaillePinceau(m->getTaillePinceau()+1);
			break;
		case GLUT_WHEEL_DOWN:
			m->setTaillePinceau(m->getTaillePinceau()-1);
			break;
	}
	MousePrevX = x;
	MousePrevY = hauteur-y;
	glutPostRedisplay() ;
}

void Callbacks::mouvement(int x,int y){
	if(modeDessin)
		m->Dessine(x,hauteur-y,MousePrevX,MousePrevY,0);
	else
		m->Dessine(x,hauteur-y,MousePrevX,MousePrevY,255);
	MousePrevX = x;
	MousePrevY = hauteur-y;
	glutPostRedisplay() ;
}

void Callbacks::mouvementPassif(int x,int y){
	MousePrevX = x;
	MousePrevY = hauteur-y;
	glutPostRedisplay() ;
}

void drawVecteur(int x,int y,vecteur v,bool c){

	//y = Callbacks::hauteur - y;

	if(c)
		glColor3f(0.0,0,1);
	else
		glColor3f(1.0,0,0);

	glLineWidth(2);
	glBegin(GL_LINE_LOOP);

		glVertex2d(x,y);
		glVertex2d(x+v.x*200,y+v.y*200);
	glEnd();


}

void Callbacks::traitementInit(){
	
	im = new Image();
	im->chargePPM("parrot.ppm");
	//im->chargePPM("a_image.ppm");
	laplacien = new Image();
	//wtf//laplacien->chargePPM("img/maguel_mask.ppm");
	

	largeur = im->getL();
	hauteur = im->getH();

	im->toSprite();

	m = new Masque(largeur,hauteur);
	m->getMaskFromPicture(im,Pixel(252,2,4));
}
void Callbacks::traitement(){
	algoPapier();
}

void Callbacks::affichage(void)
{
	glClear (GL_COLOR_BUFFER_BIT);
	im->Afficher();
	//std::cout << "bAfficherMasque = " << bAfficherMasque << std::endl;
	if ( bAfficherMasque )
	{
		//std::cout << "bAfficherMasque est vrai" << std::endl;
		m->Afficher();
		m->AffichePinceau(MousePrevX,MousePrevY);
	}
	glutSwapBuffers();
}

void Callbacks::clavier ( unsigned char key, int x, int y)
{
	Pixel t;
	switch (key)
	{
		case 'e' :
			m->Effacer();
			break;
		case ' ':
			traitement();
			break;
		case 27:
			delete im;
			delete u;
			delete u1;
			delete laplacien;
			delete laplacienU;
			delete m;
			exit(0);
			break;
			
		case 'm':
			bAfficherMasque = ! bAfficherMasque;
			//std::cout << "bAfficherMasque : " << bAfficherMasque << std::endl;
			if ( !bAfficherMasque )
				glutSetCursor(GLUT_CURSOR_INHERIT);
			else
				glutSetCursor(GLUT_CURSOR_NONE);
			break;
			
		case 't':
			inp = InpaintingThese ( Callbacks::im, Callbacks::m,
				Callbacks::im, 3 );
			inp.tout ( 0.5, 1000 );
			break;
			
		case 'c':
		{
			Image * imtemp = new Image ();
			imtemp->chargePPM ( "parrot-mask.ppm" );
			//imtemp->chargePPM ( "a_masque.ppm" );
			m->Effacer ();
			m->getMaskFromPicture(imtemp,Pixel(0,0,0));
			delete imtemp;
		}
			break;
			
		case 'w':
			std::cerr << "pls stop" << std::endl;
			inp.stop ();
			break;
			
		case 's':
			Callbacks::im->sauvePPM ( "export.ppm" );
			break;

        default:
       			break;

	}
	glutPostRedisplay();
}

void Callbacks::initGL(){

	glPixelStorei(GL_UNPACK_ALIGNMENT,1);
	glEnable(GL_BLEND);
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	glutSetCursor(GLUT_CURSOR_NONE);
	
}

void Callbacks::algoSimple(){
	double lambda = 0.2,dt=0.5;
	u1 = im->CopierImage();
	for(int nbIter = 0;nbIter <1000;nbIter++){
		delete u;
		u = u1->CopierImage();
		delete laplacienU;
		laplacienU = u->Laplacien();
		delete u1;
		u1 = new Image(largeur,hauteur);
		for(int y=0;y<hauteur;y++){
			for(int x=0;x<largeur;x++){
				int R = 1-m->LitBool(x,hauteur-y);//hauteur-y car les images sont stocké a l'envers du masque
				Pixel fp = im->LitPixel(x,y);
				Pixel up = u->LitPixel(x,y);
				Pixel ulp = laplacienU->LitPixel(x,y);
				Pixel tmp = (ulp*lambda + (fp-up)*(2.*R))*dt+up;
				u1->EcritPixel(x,y,tmp);
			}
		}
		u1->toSprite();
		u1->Afficher();
		glutSwapBuffers();
		std::cout<<nbIter<<std::endl;
	}
	u1->sauvePPM("test.ppm");
}

double min(double a){
	if ( a < 0 )
		return a;
	return 0;
}

double max(double a){
	if ( a > 0 )
		return a;
	return 0;
} 
void Callbacks::algoPapier(){
	double dt = 0.2;
	//unused var//bool up=true;
	Image* swap;
	u1 = im->CopierImage();
	u = im->CopierImage();
	//m->Traitement();
	m->empiler();

	list<Point>::iterator it;

	for(it = m->listePos.begin();it!=m->listePos.end();it++){
		//u1->EcritPixel((*it).x,hauteur-(*it).y,Pixel(0,0,0));
		//u->EcritPixel((*it).x,hauteur-(*it).y,Pixel(0,0,0));
	}

	for(int nbIter = 0;nbIter <2000;nbIter++){
		if(nbIter%15==0){
			Image* tmp = u1;
			u1 = u1->DiffusionAnisotrope(m);
			delete tmp;

		}
		/*if(nbIter%5==0&&up){
			up = m->Update();
			m->empiler();
			//dt*=0.9;
			cout<<dt<<endl;
		}*/
		swap=u1;
		u1 = u;
		u = swap;

		for(it = m->listePos.begin();it!=m->listePos.end();it++){

			int x = (*it).x;
			int y = hauteur -(*it).y;
			for(int nbMiniIter = 0;nbMiniIter<1;nbMiniIter++){

				vecteur* isophote = u->computeIsophoteDirection(x,y,1);
				vecteur* smooth = u->ComputeSmoothness(x,y);

				double r=1,v=1,b=1;

				r *= smooth[0].x*isophote[0].x+smooth[0].y*isophote[0].y;
				v *= smooth[1].x*isophote[1].x+smooth[1].y*isophote[1].y;
				b *= smooth[2].x*isophote[2].x+smooth[2].y*isophote[2].y;

				Pixel CC = (*u)(x,y);
				Pixel PC = (*u)(x-1,y);
				Pixel NC = (*u)(x+1,y);
				Pixel CP = (*u)(x,y-1);
				Pixel CN = (*u)(x,y+1);

				double rxb = CC[0]-PC[0];
				double rxf = NC[0]-CC[0];
				double ryb = CC[0]-CP[0];
				double ryf = CN[0]-CC[0];


				double vxb = CC[1]-PC[1];
				double vxf = NC[1]-CC[1];
				double vyb = CC[1]-CP[1];
				double vyf = CN[1]-CC[1];


				double bxb = CC[2]-PC[2];
				double bxf = NC[2]-CC[2];
				double byb = CC[2]-CP[2];
				double byf = CN[2]-CC[2];

				if(r>0)
					r*=sqrt(min(rxb)*min(rxb)+max(rxf)*max(rxf)+min(ryb)*min(ryb)+max(ryf)*max(ryf));
				else if (r<0)
					r*=sqrt(max(rxb)*max(rxb)+min(rxf)*min(rxf)+max(ryb)*max(ryb)+min(ryf)*min(ryf));

				if(v>0)
					v*=sqrt(min(vxb)*min(vxb)+max(vxf)*max(vxf)+min(vyb)*min(vyb)+max(vyf)*max(vyf));
				else if (v<0)
					v*=sqrt(max(vxb)*max(vxb)+min(vxf)*min(vxf)+max(vyb)*max(vyb)+min(vyf)*min(vyf));

				if(b>0)
					b*=sqrt(min(bxb)*min(bxb)+max(bxf)*max(bxf)+min(byb)*min(byb)+max(byf)*max(byf));
				else if (b<0)
					b*=sqrt(max(bxb)*max(bxb)+min(bxf)*min(bxf)+max(byb)*max(byb)+min(byf)*min(byf));


				r = CC[0] + dt*r;
				v = CC[1] + dt*v;
				b = CC[2] + dt*b;
				if(r!=0||v!=0||b!=0)
					if(r<0||r>255||v<0||v>255||b<0||b>255)
						std::cout<<r<<" "<<v<<" "<<b<<endl;
				Pixel tmp(r,v,b);
				delete []isophote;
				delete []smooth;


				u1->EcritPixel(x,y,tmp);

			}

		}
		glClear (GL_COLOR_BUFFER_BIT);
		u1->toSprite();
		u1->Afficher();
		glutSwapBuffers();

		if(nbIter%10==0)
			cout<<nbIter<<endl;

	}
	std::cout<<"fini\n";
	u1->sauvePPM("test.ppm");
}
